import os
from datetime import timedelta

from errors import Duplicate, Missing
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from model.user import User

if os.getenv("CRYPTID_UNIT_TEST"):
    from fake import user as service
else:
    from service import user as service

ACCESS_TOKEN_EXPIRE_MINUTES = 30

router = APIRouter(prefix="/user")

#
# Authn-related stuff
#

# This dependency makes a post to "/user/token"
# (from a form containing a username and password)
# and returns an access token.
oauth2_dep = OAuth2PasswordBearer(tokenUrl="token")


def _unauthd():
    raise HTTPException(
        status_code=401,
        detail="Incorrect username or password",
        headers={"WWW-Authenticate": "Bearer"},
    )


# This endpoint is directed to by any call that has the
# oauth2_dep() dependency:
@router.post("/token")
async def create_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
    """Get username and password from OAuth form, return access token"""
    user = service.auth_user(form_data.username, form_data.password)

    if not user:
        _unauthd()

    expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = service.create_access_token(
        data={"sub": user.name},
        expires=expires,
    )
    return {"access_token": access_token, "token_type": "bearer"}


@router.get("/token")
def get_access_token(token: str = Depends(oauth2_dep)) -> dict:
    """Return the current access token"""
    return {"token": token}


#
# CRUD-related stuff
#
@router.get("")
@router.get("/")
def get_all() -> list[User]:
    return service.get_all()


@router.get("/{name}")
@router.get("/{name}/")
def get_one(name: str) -> User:
    try:
        return service.get_one(name)
    except Missing as exc:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail=exc.msg
        ) from exc


@router.post("", status_code=status.HTTP_201_CREATED)
@router.post("/", status_code=status.HTTP_201_CREATED)
def create(user: User) -> User:
    try:
        return service.create(user)
    except Duplicate as exc:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT, detail=exc.msg
        ) from exc


@router.patch("/{name}")
@router.patch("/{name}/")
def modify(name: str, user: User) -> User:
    try:
        return service.modify(name, user)
    except Missing as exc:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail=exc.msg
        ) from exc


@router.delete("/{name}", status_code=status.HTTP_204_NO_CONTENT)
@router.delete("/{name}/", status_code=status.HTTP_204_NO_CONTENT)
def delete(name: str) -> None:
    try:
        return service.delete(name)
    except Missing as exc:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail=exc.msg
        ) from exc
